/*
 * Copyright (C) 2018 CyberAgent, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package jp.co.cyberagent.android.gpuvideo.sample.activity

import android.graphics.Bitmap
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.*
import android.view.View
import android.widget.SeekBar
import android.widget.SeekBar.OnSeekBarChangeListener
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import jp.co.cyberagent.android.gpuimage.filter.GPUImageFilter
import jp.co.cyberagent.android.gpuvideo.sample.R
import jp.co.cyberagent.android.gpuvideo.GPUVideoCameraView
import jp.co.cyberagent.android.gpuvideo.sample.GPUImageFilterTools
import jp.co.cyberagent.android.gpuvideo.sample.GPUImageFilterTools.FilterAdjuster
import jp.co.cyberagent.android.gpuvideo.util.Camera2Loader
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
class CameraActivity : AppCompatActivity() {

    private val gpuVideoView: GPUVideoCameraView by lazy { findViewById(R.id.surfaceView) }
    private val seekBar: SeekBar by lazy { findViewById(R.id.seekBar) }

    private var filterAdjuster: FilterAdjuster? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_camera)

        seekBar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
            override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
                filterAdjuster?.adjust(progress)
            }

            override fun onStartTrackingTouch(seekBar: SeekBar) {}
            override fun onStopTrackingTouch(seekBar: SeekBar) {}
        })
        findViewById<View>(R.id.button_choose_filter).setOnClickListener {
            GPUImageFilterTools.showDialog(this) { filter -> switchFilterTo(filter) }
        }
        findViewById<View>(R.id.button_capture).setOnClickListener {
            saveSnapshot()
        }
        findViewById<View>(R.id.img_switch_camera).run {
            if (!gpuVideoView.hasMultipleCamera()) {
                visibility = View.GONE
            }
            setOnClickListener {
                gpuVideoView.switchCamera()
            }
        }
        gpuVideoView.setDataSource(Camera2Loader(this))
    }

    override fun onResume() {
        super.onResume()
        gpuVideoView.onResume()
    }

    override fun onPause() {
        gpuVideoView.onPause()
        super.onPause()
    }

    override fun onDestroy() {
        super.onDestroy()
        gpuVideoView.release()
    }

    private fun saveSnapshot() {
        val folderName = "GPUImage"
        val fileName = System.currentTimeMillis().toString() + ".jpg"
        saveToPictures(folderName, fileName, object : OnPictureSavedListener {
            override fun onPictureSaved(uri: Uri?) {
                Toast.makeText(
                    this@CameraActivity,
                    "$folderName/$fileName saved",
                    Toast.LENGTH_SHORT
                ).show()
            }
        }
        )
    }

    private fun switchFilterTo(filter: GPUImageFilter) {
        if (gpuVideoView.filter == null || gpuVideoView.filter!!.javaClass != filter.javaClass) {
            gpuVideoView.filter = filter
            filterAdjuster = FilterAdjuster(filter)
            filterAdjuster?.adjust(seekBar.progress)
        }
    }

    /**
     * Save current image with applied filter to Pictures. It will be stored on
     * the default Picture folder on the phone below the given folderName and
     * fileName. <br></br>
     * This method is async and will notify when the image was saved through the
     * listener.
     *
     * @param folderName the folder name
     * @param fileName   the file name
     * @param listener   the listener
     */
    fun saveToPictures(
        folderName: String, fileName: String,
        listener: OnPictureSavedListener?
    ) {
        SaveTask(folderName, fileName, listener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
    }

    /**
     * Save current image with applied filter to Pictures. It will be stored on
     * the default Picture folder on the phone below the given folderName and
     * fileName. <br></br>
     * This method is async and will notify when the image was saved through the
     * listener.
     *
     * @param folderName the folder name
     * @param fileName   the file name
     * @param width      requested output width
     * @param height     requested output height
     * @param listener   the listener
     */
    fun saveToPictures(
        folderName: String, fileName: String,
        width: Int, height: Int,
        listener: OnPictureSavedListener?
    ) {
        SaveTask(
            folderName,
            fileName,
            width,
            height,
            listener
        ).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
    }

    private inner class SaveTask(
        private val folderName: String,
        private val fileName: String,
        private val width: Int,
        private val height: Int,
        private val listener: OnPictureSavedListener?
    ) :
        AsyncTask<Void?, Void?, Void?>() {
        private val handler: Handler = Handler(mainLooper)

        constructor(
            folderName: String, fileName: String,
            listener: OnPictureSavedListener?
        ) : this(folderName, fileName, 0, 0, listener) {
        }

        override fun doInBackground(vararg params: Void?): Void? {
            try {
                val result: Bitmap =
                    if (width != 0) gpuVideoView.capture(width, height) else gpuVideoView.capture()
                saveImage(folderName, fileName, result)
            } catch (e: InterruptedException) {
                e.printStackTrace()
            }
            return null
        }

        private fun saveImage(folderName: String, fileName: String, image: Bitmap) {
            val path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
            val file = File(path, "$folderName/$fileName")
            try {
                file.parentFile.mkdirs()
                image.compress(Bitmap.CompressFormat.JPEG, 80, FileOutputStream(file))
                MediaScannerConnection.scanFile(
                    this@CameraActivity, arrayOf(
                        file.toString()
                    ), null
                ) { path1: String?, uri: Uri? ->
                    if (listener != null) {
                        handler.post { listener.onPictureSaved(uri) }
                    }
                }
            } catch (e: FileNotFoundException) {
                e.printStackTrace()
            }
        }

    }

    interface OnPictureSavedListener {
        fun onPictureSaved(uri: Uri?)
    }
}
